package filejoiner.util;

import filejoiner.data.DataSheet;
import filejoiner.data.Entry;
import filejoiner.data.WorkPath;
import filejoiner.method.Method;
import filejoiner.method.MethodLoader;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author Luis
 */
public class DataSheetUtils {
    
    private MethodLoader methodLoader = new MethodLoader();
    private List<DataSheetListener> listeners = new ArrayList<DataSheetListener>();
    
    public DataSheet splitFileAndMask(File file, File mask, Integer fileSize, WorkPath workPath) throws FileNotFoundException, IOException, Exception {
        FileUtils fileUtils = new FileUtils(file, fileSize);
        DataSheet dataSheet = new DataSheet();
        dataSheet.setFileName(file.getName());
        dataSheet.setFileSize(fileSize);
        ArrayList<Entry> entrys = new ArrayList<Entry>();
        Integer x = 0;
        while (fileUtils.hasNext()) {
            try {
                byte[] fileMasked = fileUtils.mask(mask);
                Entry entry = new Entry();
                entry.setPosition(x);
                Pattern pattern = Pattern.compile("(.*)([.][a-zA-Z]*)");
                Matcher matcher = pattern.matcher(mask.getName());
                entry.setName(matcher.replaceAll(StringUtils.random() + "$2"));
                entry.setMaskSize((int) mask.length());
                fileUtils.write(fileMasked, workPath.getTempPath(), entry.getName());
                File fileRead = new File(workPath.getTempPath() + entry.getName());
                entry.setFileSize((int) fileRead.length());
                entry.setHashCode(MD5Checksum.getMD5Checksum(fileRead));
                entrys.add(entry);
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onSplit(dataSheet, entry);
                }
                x++;
            } catch (Exception e) {
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onSplitError(dataSheet, dataSheet.getEntrys().get(x));
                }
            }
        }
        dataSheet.setEntrys(entrys);
        return dataSheet;
    }
    
    public void upload(DataSheet dataSheet, Method method, WorkPath workPath) {
        List<Entry> entrys = dataSheet.getEntrys();
        for (Entry entry : entrys) {
            File file = new File(workPath.getTempPath() + entry.getName());
            if (!method.upload(entry, file, workPath)) {
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onUploadError(dataSheet, entry);
                }
            }
            for (DataSheetListener dataSheetListener : listeners) {
                dataSheetListener.onUpload(dataSheet, entry);
            }
        }
    }
    
    public void download(DataSheet dataSheet, WorkPath workPath) throws Exception {
        List<Entry> entrys = dataSheet.getEntrys();
        for (Entry entry : entrys) {
            try {
                methodLoader.loadMethod(entry).Download(entry, workPath);
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onDownload(dataSheet, entry);
                }
            } catch (Exception e) {
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onDownloadError(dataSheet, entry);
                }
            }
        }
    }
    
    public void makeFile(DataSheet dataSheet, WorkPath workPath) throws Exception {
        List<Entry> entrys = dataSheet.getEntrys();
        File out = new File(workPath.getPath() + dataSheet.getFileName());
        FileOutputStream fos = new FileOutputStream(out);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos);
        for (Entry entry : entrys) {
            if (validateEntry(dataSheet, entry, workPath)) {
                try {
                    File file = new File(workPath.getTempPath() + entry.getName());
                    byte[] bs = FileUtils.getBytes(file);
                    byte[] copyOfRange = Arrays.copyOfRange(bs, entry.getMaskSize(), bs.length);
                    bufferedOutputStream.write(copyOfRange);
                    for (DataSheetListener dataSheetListener : listeners) {
                        dataSheetListener.onMake(dataSheet, entry);
                    }
                } catch (IOException ioe) {
                    for (DataSheetListener dataSheetListener : listeners) {
                        dataSheetListener.onMakeError(dataSheet, entry, DataSheetListener.FILE_ERROR);
                    }
                }
            }
        }
        bufferedOutputStream.close();
    }
    
    public Boolean validateEntry(DataSheet dataSheet, Entry entry, WorkPath workPath) throws Exception {
        File file = new File(workPath.getTempPath() + entry.getName());
        if (file.exists()) {
            if (!MD5Checksum.getMD5Checksum(file).equals(entry.getHashCode())) {
                for (DataSheetListener dataSheetListener : listeners) {
                    dataSheetListener.onMakeError(dataSheet, entry, DataSheetListener.HASH_ERROR);
                }
                return false;
            }
        } else {
            for (DataSheetListener dataSheetListener : listeners) {
                dataSheetListener.onMakeError(dataSheet, entry, DataSheetListener.NOT_EXIST_ERROR);
            }
            return false;
        }
        return true;
    }
    
    public void clean(DataSheet dataSheet, WorkPath workPath) {
        List<Entry> entrys = dataSheet.getEntrys();
        for (Entry entry : entrys) {
            File file = new File(workPath.getTempPath() + entry.getName());
            if (file.exists()) {
                file.delete();
            }
        }
    }
    
    public MethodLoader getMethodLoader() {
        return methodLoader;
    }
    
    public void setMethodLoader(MethodLoader methodLoader) {
        this.methodLoader = methodLoader;
    }
    
    public void addMethod(Method method) {
        methodLoader.getMethods().add(method);
    }
    
    public void addDataSheetListener(DataSheetListener dataSheetListener) {
        listeners.add(dataSheetListener);
    }
    
    public void removeDataSheetListener(DataSheetListener dataSheetListener) {
        listeners.remove(dataSheetListener);
    }
}
